﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using TurtleLib.Simulation;

namespace TurtleLib.ComputerCraft
{
  public partial class PeripheralUtility
  {
    private Func<Point3D> WorldLocationRequestCallback;
    private Func<Face> WorldFaceRequestCallback;
    private Func<Point3D, object> WorldObjectLookupCallback;
    
    internal PeripheralUtility()
    {
    }

    /// <summary>
    /// Peripheral exists adjacent to parent object
    /// </summary>
    /// <param name="side">face to check</param>
    /// <returns>true if peripheral is found</returns>
    public bool isPresent(Side side)
    {
      throw new NotImplementedException();
    }

    /// <summary>
    /// rx, ry, and rz are RELATIVE points to the side
    /// </summary>
    private void GetRelativeOffset(Side side, out int rx, out int ry, out int rz)
    {
      rx = (side == Side.Right ? 1 : side == Side.Left ? -1 : 0);
      ry = (side == Side.Top ? 1 : side == Side.Bottom ? -1 : 0);
      rz = (side == Side.Back ? 1 : side == Side.Front ? -1 : 0);
    }

    /// <summary>
    /// Returns a handle the the peripheral located on side. The handle then allows direct use of the peripheral.
    /// e.g.
    /// m = peripheral.wrap(World.Side.Left)
    /// m.someMethod(arg1, arg2)
    /// - is the same as -
    /// peripheral.call(World.Side.Left, "someMethod", arg1, arg2)
    /// </summary>
    /// <param name="side">face to wrap</param>
    /// <returns>handle</returns>
    public IPeripheral wrap(Side side)
    {
      Point3D worldPosition = WorldLocationRequestCallback();
      Face facing = WorldFaceRequestCallback();

      int ax = 0;
      int ay = 0;
      int az = 0;
      switch (facing)
      {
        case Face.North:
        case Face.South: GetRelativeOffset(side, out ax, out ay, out az); break;
        case Face.East:
        case Face.West: GetRelativeOffset(side, out az, out ay, out ax); break;
      }

      // turtle and computer cannot face up/down

      if (facing == Face.North || facing == Face.West)
      {
        ax *= -1;
        az *= -1;
      }

      worldPosition = new Point3D
      (
        x: worldPosition.X + ax,
        y: worldPosition.Y + ay,
        z: worldPosition.Z + az
      );

      object worldObject = WorldObjectLookupCallback(worldPosition);

      var peripheral = worldObject as IPeripheral;
      if (peripheral is Monitor)
      {
        var monitor = peripheral as Monitor;
        peripheral = monitor.GetHost();
      }

      return peripheral;
    }

    /// <summary>
    /// Helper method for Wrap
    /// </summary>
    /// <typeparam name="T">Expected type of peripheral</typeparam>
    /// <param name="side">face to wrap</param>
    /// <returns>handle strong typed as T, or throws type cast exception if type doesn't match</returns>
    public T wrap<T>(Side side) where T : IPeripheral
    {
      return (T)(wrap(side));
    }

    /// <summary>
    /// Returns an array containing all methods for peripheral on side
    /// </summary>
    /// <param name="side">side to analyze</param>
    /// <returns>array of methods</returns>
    public MethodInfo[] getMethods(Side side)
    {
      throw new NotImplementedException();
    }
  }
}
